﻿using HalconDotNet;
using Halcon二次开发.Model;
using Halcon二次开发.Model.储存每个Controller的单例对象;
using Halcon二次开发.Model.自定义控件;
using Halcon二次开发.Tools;
using Halcon二次开发.View.OCR字符识别;
using Sunny.UI.Win32;
using System;
using System.Collections.Generic;
using System.Drawing.Printing;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Halcon二次开发.Controller.OCR字符识别
{
    public class OCRFormController
    {
        /// <summary>
        /// 保存路径
        /// </summary>
        string path;
        /// <summary>
        /// OCR界面
        /// </summary>
        OCRForm form;
        /// <summary>
        /// 矩形2工具包
        /// </summary>
        GenRectangle2 drawRectangle2;
        /// <summary>
        /// 矩形2工具包
        /// </summary>
        GenCirle drawCirle;
        /// <summary>
        /// 窗体显示控件句柄
        /// </summary>
        HWindow hWindon,OCR_hWindow,Char_OCR_hWindow,Char_hWindow;
        /// <summary>
        /// 存入基准数据只存入一次
        /// </summary>
        bool StandarCheck = true;
        /// <summary>
        /// 储存所有字符
        /// </summary>
        public List<HObject> CharDb = new List<HObject>();
        /// <summary>
        /// 扶正后的图像
        /// </summary>
        HObject StraighteningImage;
        /// <summary>
        /// 字符灰度图片
        /// </summary>
        HObject ThreadImage;
        /// <summary>
        /// 当前选中的字符
        /// </summary>
        HObject CurrentChar;
        /// <summary>
        /// 当前字符选中的下标
        /// </summary>
        int CharIndex;
        #region 控制参数
        public HObject IntputImage; //输入图片
        public double StandardPhi;
        public string OutputString; //输出结果
        #endregion
        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="ocr"></param>
        public OCRFormController(OCRForm ocr,string path) { 
            this.form = ocr;
            //当前窗体显示控件的句柄
            this.hWindon = StaicResouce.HwindowFlowNodeUrse.hSmartWindowControl1.HalconWindow;
            //当前工具的显示控件句柄
            this.OCR_hWindow = form.hSmartWindowControl1.HalconWindow;
            this.Char_OCR_hWindow = form.hSmartWindowControl2.HalconWindow;
            this.Char_hWindow = form.hSmartWindowControl3.HalconWindow;
            //调用工具包-矩形2
            drawRectangle2 = new GenRectangle2();
            drawCirle = new GenCirle();
            this.path = path;   
            HOperatorSet.GenEmptyObj(out StraighteningImage);

            //创建文件夹
            if (!Directory.Exists(path))
            {
                Directory.CreateDirectory(path);
            }
        }

        /// <summary>
        /// 画矩形
        /// </summary>
        /// <param name="hWindon">句柄</param>
        public void DrawRectangle2()
        {
            //互锁
            drawCirle.ClearObject(hWindon);
            Rectangle2Control rectangle2 = (Rectangle2Control)form.groupBox3.Controls[0];
            double x = double.Parse(rectangle2.XUpDown.Value + "");
            double y = double.Parse(rectangle2.YUpDown.Value + "");
            double phi = double.Parse(rectangle2.PhiUpDown.Value + "");
            double length1 = double.Parse(rectangle2.Length1UpDown.Value + "");
            double length2 = double.Parse(rectangle2.Length2UpDown.Value + "");
            drawRectangle2.DrawRectangle2(hWindon, x, y, phi, length1, length2, form.textBox3.Text, rectangle2.XUpDown, rectangle2.YUpDown, rectangle2.PhiUpDown, rectangle2.Length1UpDown, rectangle2.Length2UpDown);
        }
        /// <summary>
        /// 画圆
        /// </summary>
        /// <param name="hWindon">句柄</param>
        public void DrawCirle()
        {
            //互锁
            drawRectangle2.ClearObject(hWindon);
            CirleControl cirle = (CirleControl)form.groupBox3.Controls[0];
            double x = double.Parse(cirle.XUpDown.Value + "");
            double y = double.Parse(cirle.YUpDown.Value + "");
            double radius = double.Parse(cirle.RaduisUpDown.Value + "");
            drawCirle.DrawCirle(hWindon, x, y, radius, form.textBox3.Text, cirle.XUpDown, cirle.YUpDown, cirle.RaduisUpDown);
        }
        /// <summary>
        /// 显示错误信息
        /// </summary>
        /// <returns></returns>
        public bool ImageIsNg()
        {
            foreach (TreeNode node in StaicResouce.HwindowFlowNodeUrse.treeView1.Nodes)
            {
                if (node.Nodes.Count == 0) continue;
                if (node.Nodes[0].Tag == this && node.Nodes[0].Text.Contains("<—"))
                {
                    return false;
                }
            }
            return true;
        }

        /// <summary>
        /// 计量模型移动方法
        /// </summary>
        public void Move(Object sender)
        {
            NumericUpDown number = (sender as NumericUpDown);
            UserControl control = (UserControl)number.Parent;
            if (control.Name == "Rectangle2Control")
            {
                Rectangle2Control rectangle = (Rectangle2Control)number.Parent;
                if (rectangle != null)
                {
                    drawRectangle2.Move(hWindon, double.Parse(rectangle.XUpDown.Value + ""), double.Parse(rectangle.YUpDown.Value + ""), double.Parse(rectangle.PhiUpDown.Value + ""), double.Parse(rectangle.Length1UpDown.Value + "")
                        , double.Parse(rectangle.Length2UpDown.Value + ""), form.textBox3.Text);
                }
            }
            else if (control.Name == "CirleControl")
            {
                CirleControl cirle = (CirleControl)number.Parent;
                if (control != null)
                {
                    drawCirle.Move(hWindon, double.Parse(cirle.XUpDown.Value + ""), double.Parse(cirle.YUpDown.Value + ""), double.Parse(cirle.RaduisUpDown.Value + ""), form.textBox3.Text);
                }
            }
        }

        /// <summary>
        /// 清除模型
        /// </summary>
        /// <param name="hWindon"></param>
        public void ClearTwoObject(HWindow hWindon)
        {
            //清除模型
            if (drawRectangle2 != null)
            {
                drawRectangle2.ClearObject(hWindon);
            }

            if (drawCirle != null)
            {
                drawCirle.ClearObject(hWindon);
            }
        }

        /// <summary>
        /// 工具的运行
        /// </summary>
        public void Run()
        {
            SetRegion();
            ExtractLine();
            OutputString = form.textBox4.Text;
        }
        /// <summary>
        /// 提取线
        /// </summary>
        public void ExtractLine() {
            //储存字符识别的容器
            List<HObject> listdb = new List<HObject>();
            //显示扶正后的内容
            HOperatorSet.ClearWindow(OCR_hWindow);
            HOperatorSet.ClearWindow(Char_OCR_hWindow);

            HOperatorSet.Rgb1ToGray(StraighteningImage,out HObject grayImage);

            //TODO: 这里要对外设置一个接口,用来筛出的黑边rgb值,这里先设置为0
            //筛出黑边的边框
            HOperatorSet.Threshold(grayImage, out HObject threadImage,1,255);
            HOperatorSet.ReduceDomain(grayImage, threadImage ,out HObject reduceImage);
            //给字符显示区域
            HOperatorSet.ScaleImage(reduceImage,out HObject imageScaled, double.Parse(form.BackGroundProportion.Value.ToString()), double.Parse(form.BackGroundOffset.Value.ToString()));

            HOperatorSet.CropDomain(imageScaled,out HObject imagePart);

            ThreadImage = imagePart;
            //显示图像
            HOperatorSet.DispObj(imagePart, OCR_hWindow);
            form.hSmartWindowControl1.SetFullImagePart();

            HOperatorSet.DispObj(imagePart, Char_OCR_hWindow);
            form.hSmartWindowControl2.SetFullImagePart();

            //给提取黑字还是白字,还是其它颜色字体
            HOperatorSet.Threshold(imagePart, out HObject thread2Image, int.Parse(form.MinForeValue.Value.ToString()), int.Parse(form.MaxForeValue.Value.ToString()));


            //打断区域
            HOperatorSet.Connection(thread2Image, out HObject connectedRegions);

            //筛选字体最小宽度高度
            HOperatorSet.SelectShape(connectedRegions,out HObject selectedRegions, (new HTuple("width")).TupleConcat("height"), "and", 
                (new HTuple(int.Parse(form.MinWidth.Value.ToString()))).TupleConcat(int.Parse(form.MinHeight.Value.ToString())), 
                (new HTuple(int.Parse(form.MaxWidth.Value.ToString()))).TupleConcat(int.Parse(form.MaxHeight.Value.ToString())));


            //给区域排序
            HOperatorSet.SortRegion(selectedRegions, out HObject sortedTegions, "first_point","true", "column");
            //求出区域数量  reRegionCount: 区域的数量
            HOperatorSet.CountObj(sortedTegions, out HTuple hv_Number);
            
            //储存已经合并的区域
            List<HObject> alreadyDb = new List<HObject>();
            HOperatorSet.SetDraw(OCR_hWindow, "margin");
            HOperatorSet.SetDraw(Char_OCR_hWindow, "margin");
            HOperatorSet.SetColor(OCR_hWindow,"red");
            HOperatorSet.SetColor(Char_OCR_hWindow,"red");

            HObject ho_Rectangle2 = null;
            for (int i = 1; i <= hv_Number; i++)
            {
                //创建区域 比较
                HOperatorSet.SelectObj(sortedTegions, out HObject ho_OneRegion, i);
                //膨胀
                HOperatorSet.DilationCircle(ho_OneRegion, out HObject ho_RegionDilation1, double.Parse(form.CirleSwellProportion.Value.ToString()));
                HOperatorSet.DilationRectangle1(ho_RegionDilation1, out ho_RegionDilation1, double.Parse(form.WidthSwellProportion.Value.ToString()), double.Parse(form.HeigthSwellProportion.Value.ToString()));
                HOperatorSet.SmallestRectangle2(ho_RegionDilation1, out HTuple hv_Row1, out HTuple hv_Column1,
                    out HTuple hv_Phi1, out HTuple hv_Length11, out HTuple hv_Length21);

                HOperatorSet.GenRectangle2(out ho_Rectangle2, hv_Row1, hv_Column1, hv_Phi1,
                    hv_Length11, hv_Length21);

                //这里表示该区域已经合并了
                int peper = -1;
                foreach (var item in alreadyDb)
                {
                    HOperatorSet.AreaCenter(item, out HTuple area1, out HTuple row1, out HTuple column1);
                    HOperatorSet.AreaCenter(ho_OneRegion, out HTuple area2, out HTuple row2, out HTuple column2);
                    if (row1.D.ToString("F0") == row2.D.ToString("F0") && column1.D.ToString("F0") == column2.D.ToString("F0"))
                    {
                        peper = 1;
                        break;
                    }
                }

                if (peper != -1) continue;

                for (int j = 1; j <= hv_Number; j++)
                {
                    //创建区域 被比较
                    HOperatorSet.SelectObj(sortedTegions, out HObject ho_TwoRegion, j);
                    //这里要排除自己
                    HOperatorSet.Difference(ho_OneRegion,ho_TwoRegion,out HObject regionDifference);
                    HOperatorSet.AreaCenter(regionDifference,out HTuple areaDiff,out HTuple rowDiff, out HTuple columnDiff);
                    if (areaDiff == 0) continue;

                    //膨胀
                    HOperatorSet.DilationCircle(ho_TwoRegion, out HObject ho_RegionDilation2, double.Parse(form.CirleSwellProportion.Value.ToString()));
                    HOperatorSet.DilationRectangle1(ho_RegionDilation2, out ho_RegionDilation2, double.Parse(form.WidthSwellProportion.Value.ToString()), double.Parse(form.HeigthSwellProportion.Value.ToString()));
                    HOperatorSet.SmallestRectangle2(ho_RegionDilation2, out hv_Row1, out hv_Column1,
                        out hv_Phi1, out hv_Length11, out hv_Length21);
                    HOperatorSet.GenRectangle2(out HObject ho_Rectangle1, hv_Row1, hv_Column1, hv_Phi1,
                        hv_Length11, hv_Length21);
                    //算交集
                    HOperatorSet.Intersection(ho_Rectangle1, ho_Rectangle2, out HObject ho_RegionIntersection);
                    HOperatorSet.AreaCenter(ho_RegionIntersection, out HTuple area, out HTuple row, out HTuple column);
                    if (area > 0)
                    {
                        //区域相交的合并
                        {
                            alreadyDb.Add(ho_TwoRegion);    
                            HOperatorSet.Union2(ho_Rectangle1, ho_Rectangle2, out ho_Rectangle2);
                        }
                    }
                }
                HOperatorSet.SmallestRectangle2(ho_Rectangle2, out HTuple hv_Row2, out HTuple hv_Column2,
                            out HTuple hv_Phi2, out HTuple hv_Length12, out HTuple hv_Length22);
                HOperatorSet.GenRectangle2(out HObject ho_Rectangle3, hv_Row2, hv_Column2, hv_Phi2,
                    hv_Length12, hv_Length22);
                //显示区域
                listdb.Add(ho_Rectangle3);
            }
            //过滤重叠的区域集合
            List<HObject> db = new List<HObject>();
            List<HObject> removeDb = new List<HObject>();
            foreach (HObject ho1 in listdb)
            {
                foreach (HObject ho2 in listdb)
                {
                    if (ho1 == ho2) continue;
                    HOperatorSet.Intersection(ho1, ho2, out HObject ho_RegionIntersection);
                    HOperatorSet.AreaCenter(ho_RegionIntersection, out HTuple area, out HTuple row, out HTuple column);
                    if (area > 0)
                    {
                        HOperatorSet.AreaCenter(ho1, out HTuple area1, out HTuple row1, out HTuple column1);
                        HOperatorSet.AreaCenter(ho2, out HTuple area2, out HTuple row2, out HTuple column2);
                        //区域相交的合并
                        if (area1 > area2)
                        {
                            removeDb.Add(ho2);
                        }
                        else removeDb.Add(ho1);
                    }
                    
                }
            }

            foreach (var item in removeDb)
            {
                listdb.Remove(item);
            }


            foreach (HObject ho in listdb)
            {
                HOperatorSet.DispObj(ho, OCR_hWindow);
                HOperatorSet.DispObj(ho, Char_OCR_hWindow);
            }
            //清除上次运行结果
            CharDb.Clear();
            form.textBox4.Clear();
            HTuple oCRHandle = GetOCRChar();
            //截取出来
            foreach (HObject ho in listdb) {
                HOperatorSet.ReduceDomain(imagePart, ho,out HObject imageReduced);
                //HOperatorSet.CropDomain(imageReduced, out HObject imagePart11);

                //先识别字符
                if (oCRHandle != null)
                {
                    HOperatorSet.Rgb1ToGray(ThreadImage, out HObject charregion);
                    //这里要将截取图形转为一个区域变量
                    HOperatorSet.Threshold(imageReduced, out HObject region , int.Parse(form.MinForeValue.Value.ToString()), int.Parse(form.MaxForeValue.Value.ToString()));

                    //HOperatorSet.ClearWindow(Char_hWindow);
                    //HOperatorSet.DispObj(region, Char_hWindow);
                    //form.hSmartWindowControl3.SetFullImagePart();
                    //continue;

                    HOperatorSet.DoOcrMultiClassMlp(region, charregion, oCRHandle, out HTuple classVal, out HTuple confidence);
                    //添加字符
                    if (classVal.Length > 0)
                    {
                        form.textBox4.AppendText(classVal.S);
                    }
                    else
                    {
                        form.textBox4.AppendText("?");
                    }
                }
                else {
                    form.textBox4.AppendText("?");
                }
                
                
                CharDb.Add(imageReduced);
            }
            if (listdb.Count==0)
            {
                form.textBox4.AppendText("?");
            }
            HOperatorSet.SetDraw(OCR_hWindow, "fill");
        }
        /// <summary>
        /// 读取字符,返回一个句柄
        /// </summary>
        /// <returns></returns>
        public HTuple GetOCRChar() {
            //这里要读这个trf文件
            string str = path + "\\" + form.Text + ".trf";
            if(!File.Exists(str))return null;
            //读取字符集
            HOperatorSet.ReadOcrTrainfNames(str, out HTuple characterNames, out HTuple characterCount);
            //创建OCR分类器
            HOperatorSet.CreateOcrClassMlp(20, 20, "bicubic", new string[] { "pixel" ,"pixel_invar"}, characterNames, 20, "normalization", 26, 42, out HTuple oCRHandle);
            //训练OCR
            HOperatorSet.TrainfOcrClassMlp(oCRHandle, str, 500, 1, 0.00001, out HTuple error, out HTuple errorLog);

            return oCRHandle;
        }
        /// <summary>
        /// 放置区域(抠图)
        /// </summary>
        public void SetRegion() {
            try
            {
                if (IntputImage == null)
                {
                    throw new Exception("输入图像为null!");
                }
                string str = form.toolStripComboBox1.SelectedItem.ToString();
                if (str == "旋转矩形")
                {
                    double row = double.Parse(form.node1.XUpDown.Value + "");
                    double col = double.Parse(form.node1.YUpDown.Value + "");
                    double length1 = double.Parse(form.node1.Length1UpDown.Value + "");
                    double length2 = double.Parse(form.node1.Length2UpDown.Value + "");
                    double phi = double.Parse(form.node1.PhiUpDown.Value + "");
                    //Phi就是基本的弧度
                    HOperatorSet.GenRectangle2(out HObject Roi_0, row, col, phi, length1, length2);
                    //开始抠图
                    HOperatorSet.ReduceDomain(IntputImage, Roi_0, out HObject Reduce_Image);
                    //更改抠图后图像大小
                    HOperatorSet.CropDomain(Reduce_Image, out HObject imagePart);
                    HOperatorSet.GetImageSize(imagePart, out HTuple width, out HTuple height);
                    //回正图片 
                    //创建一个齐次矩阵
                    HOperatorSet.HomMat2dIdentity(out HTuple hv_HomMat2DIdentity1);
                    HTuple hv_HomMat2DRotate1;
                    using (HDevDisposeHelper dh = new HDevDisposeHelper())
                    {
                        //旋转: 想要图像摆正,弧度直接用0-当前图像的弧度即可 ,这里以图像的长度宽度除以2,就是他们的中心旋转
                        HOperatorSet.HomMat2dRotate(hv_HomMat2DIdentity1, 0 - phi, width / 2, height / 2,
                            out hv_HomMat2DRotate1);
                    }
                    HOperatorSet.AffineTransImage(imagePart, out StraighteningImage, hv_HomMat2DRotate1,
                        "constant", "true");
                }
                else if (str == "圆形")
                {

                }///后续有更多矩形在这里拓展
            }
            catch (Exception ex){
                throw new Exception(ex.Message);
            }
        }

        /// <summary>
        /// 工具的单次运行
        /// </summary>
        public void Tool_Run()
        {
            if (IntputImage == null || ImageIsNg())
            {
                throw new Exception("当前图像为空");
            }
            hWindon.ClearWindow();
            HOperatorSet.DispObj(IntputImage, hWindon);
            Run();
        }

        /// <summary>
        /// 显示控件点击事件
        /// </summary>
        public void Mouse_MouseClick(object sender, HalconDotNet.HMouseEventArgs e) {
            HOperatorSet.SetColor(Char_hWindow,"red");
            HOperatorSet.SetDraw(Char_hWindow,"margin");
            Char_hWindow.SetWindowParam("background_color", "blue");
            HTuple oCRHandle = GetOCRChar();
            double x = e.X;
            double y = e.Y;
            int count = 0;
            foreach (HObject item in CharDb)
            {
                //判断两点是否在该区域内
                HOperatorSet.GetImageSize(item, out HTuple width1, out HTuple height1);
                HOperatorSet.CropDomain(item, out HObject imagePart1);
                HOperatorSet.TestRegionPoint(item, y, x,out HTuple Isindex);
                if (Isindex!=0) 
                {
                    form.textBox6.Clear();
                    HOperatorSet.ClearWindow(Char_hWindow);
                    HOperatorSet.CropDomain(item, out HObject imagePart);
                    HOperatorSet.HomMat2dIdentity(out HTuple hv_HomMat2DIdentity1);
                    HOperatorSet.GetImageSize(imagePart, out HTuple width, out HTuple height);
                    HOperatorSet.HomMat2dScale(hv_HomMat2DIdentity1, 1, 1, height / 2, width / 2, out HTuple homMat2DScale);
                    HOperatorSet.AffineTransImage(imagePart, out HObject scaleImage, homMat2DScale,
                    "constant", "true");
                    //要以这个为识别字符的图片,经过二值化处理后的
                    HOperatorSet.Threshold(item, out HObject charregion, int.Parse(form.MinForeValue.Value.ToString()), int.Parse(form.MaxForeValue.Value.ToString()));
                    //这里要灰度图
                    HOperatorSet.Rgb1ToGray(ThreadImage,out HObject grayImage);
                    HOperatorSet.DispObj(scaleImage, Char_hWindow);
                    form.hSmartWindowControl3.SetFullImagePart();

                    CharIndex = count;

                    //HOperatorSet.ClearWindow(Char_hWindow);
                    //HOperatorSet.DispObj(ThresholdImage, Char_hWindow);
                    //form.hSmartWindowControl3.SetFullImagePart();
                    //break;
                    CurrentChar = charregion;
                    if (oCRHandle != null)
                    {
                       
                        //先识别字符
                        HOperatorSet.DoOcrMultiClassMlp(charregion, grayImage, oCRHandle, out HTuple classVal, out HTuple confidence);
                        //添加字符
                        if (classVal.Length > 0)
                        {
                            form.textBox6.AppendText(classVal.S);
                        }
                        else
                        {
                            form.textBox6.AppendText("?");
                        }
                    }
                    else
                    {
                        form.textBox6.AppendText("?");
                    }
                    break;
                }
                count++;
            }
        }
        /// <summary>
        /// 添加多个
        /// </summary>
        public void AddChars() {
            int count = 0;
            char[] chars = form.textBox4.Text.ToCharArray();
            string str = path+"\\"+form.Text + ".trf";
            if (chars.Length != CharDb.Count||chars.Contains('?')) {
                form.textBox4.Clear();
                foreach (var item in CharDb)
                {
                    form.textBox4.AppendText("?");
                }
                return;
            }
            foreach (var item in CharDb)
            {
                //这里先二值化提取字符区域出来进行存入
                HOperatorSet.Threshold(item, out HObject charregion, int.Parse(form.MinForeValue.Value.ToString()), int.Parse(form.MaxForeValue.Value.ToString()));

                //Char_OCR_hWindow.SetWindowParam("background_color", "blue");
                //HOperatorSet.ClearWindow(Char_OCR_hWindow);
                //HOperatorSet.DispObj(charregion, Char_OCR_hWindow);
                //form.hSmartWindowControl2.SetFullImagePart();
                //break;

                HOperatorSet.AppendOcrTrainf(charregion, ThreadImage, chars[count].ToString(), str);
                count++;
            }
            form.hSmartWindowControl3.SetFullImagePart();
        }
        /// <summary>
        /// 添加单个
        /// </summary>
        public void AddChar() {
            string str = path + "\\" + form.Text + ".trf";

            //HOperatorSet.ClearWindow(Char_hWindow);
            //HOperatorSet.DispObj(CurrentChar, Char_hWindow);
            //form.hSmartWindowControl3.SetFullImagePart();
            //return;

            HOperatorSet.AppendOcrTrainf(CurrentChar, ThreadImage, form.textBox6.Text.ToString(), str);
            
            string a = form.textBox4.Text.Substring(0,CharIndex);
            string b = form.textBox4.Text.Substring(CharIndex+1);
            string text = a + form.textBox6.Text[0] +b;
            text = text.Replace(form.textBox6.Text[0], form.textBox6.Text[0]);
            form.textBox4.Text = text;
        }
        /// <summary>
        /// 删除所有项
        /// </summary>
        public void ClearChar() {
            string str = path + "\\" + form.Text + ".trf";
            if (File.Exists(str))
            {
                File.Delete(str);
            }
        }
        /// <summary>
        /// 打开文件
        /// </summary>
        public void Open_File() {
            string str = path + "\\" + form.Text + ".trf";
            HOperatorSet.ReadOcrTrainfNames(str,out HTuple characterNames,out HTuple characterCount);
        }
    }
}
